Skip to content

fix: react #467 crash in useValueRef on suspense replay - #12499

Merged
guanbinrui merged 1 commit into
developfrom
fix/use-value-ref-react-467
Sep 30, 2026
Merged

guanbinrui merged 1 commit into
developfrom
fix/use-value-ref-react-467

Conversation

@guanbinrui

Copy link
Copy Markdown
Member

Problem

Production crash (v2.37.0) on the Mask Network entrance in the X sidebar:

Error: Minified React error #467 (Update hook called on initial render)
    at updateSyncExternalStore
    at useValueRef
    at useGuideStepState
    at ToolboxHintForWallet
    at renderWithHooksAgain / replaySuspendedUnitOfWork

Root cause

useValueRef called use(readyPromise) only while !ref.ready. useGuideStepState reads two refs in a row. If the second ref isn't ready, the component suspends. When React replays it after the promise resolves, ready is already true, so use is skipped. React only switches from the update dispatcher back to the mount dispatcher inside use, so the next useSyncExternalStore hits updateWorkInProgressHook with no current fiber and throws #467.

Fix

  • useValueRef: always call use(readyPromise) for ValueRefWithReady, so the hook order is the same on every render and replay.
  • ValueRefWithReady: once readyPromise resolves, mark it with status: 'fulfilled' and value (React's thenable protocol). That way use returns synchronously for refs that are already ready instead of suspending briefly.

Testing

  • eslint clean on the changed files; the changed files type-check
  • Manual: load x.com with a fresh profile and confirm the sidebar entrance renders without the crash screen

🤖 Generated with Claude Code

useValueRef only called `use(readyPromise)` while the ref was not ready.
When a component suspended there and was replayed after the promise
resolved, `ready` had flipped to true so `use` was skipped; React only
switches back to the mount dispatcher inside `use`, so the following
useSyncExternalStore threw "Update hook called on initial render"
(crashing the Mask Network entrance in the X sidebar via
ToolboxHintForWallet -> useGuideStepState).

- Always call `use(readyPromise)` for ValueRefWithReady.
- Mark readyPromise as fulfilled (React thenable protocol) so `use`
  returns synchronously once ready instead of suspending briefly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
book Ready Ready Preview Sep 30, 2026 5:06am UTC

Request Review

@guanbinrui
guanbinrui merged commit c98521d into develop Sep 30, 2026
11 checks passed
@guanbinrui
guanbinrui deleted the fix/use-value-ref-react-467 branch September 30, 2026 05:08
@Jack-Works

Copy link
Copy Markdown
Member

This is strange, since react document (https://react.dev/reference/react/use#use-promise) states:

Call use with a Promise to read its resolved value. The component calling use suspends while the Promise is pending. Despite its name, use is not a Hook. Unlike Hooks, it can be called inside loops and conditional statements like if.

This branch was successfully deployed

1 active deployment
Preview — d7ce732d Deployed Sep 30, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants